/**
 * Java引入了包（package）机制，提供了类的多层命名空间，用于解决类的命名冲突、类文件管理等问题。
 * Java允许将一组功能相关的类放在同一个package下，从而组成逻辑上的类库单元。
 *
 * <p>编译：javac -encoding UTF-8 -d out src/lee/Hello.java
 * <p>运行：java -classpath out lee.Hello
 */
package lee;

/**
 * 上面一行代码行表明把Hello类放在lee包空间下。
 * 编译：javac -encoding UTF-8 -d out src/lee/Hello.java
 * -d选项用于设置编译生成class文件的保存位置，这里指定将生成的class文件放在当前out路径下。
 * 使用该命令编译该文件后，发现out路径下并没有Hello.class文件，而是在out路径下多了一个名为lee的文件夹，该文件夹下则有一个Hello.class文件。
 * <p>
 * 这是怎么回事呢？这与Java的设计有关。假设某个应用中包含两个Hello类，Java通过引入包机制来区分两个不同的Hello类。
 * 不仅如此，这两个Hello类还对应两个Hello.class文件，它们在文件系统中也必须分开存放才不会引起冲突。
 * 所以Java规定：位于包中的类，在文件系统中也必须有与包名层次相同的目录结构。
 * <p>
 * 对于上面的Hello.class，它必须放在lee文件夹下才是有效的，当使用带-d选项的javac命令来编译Java源文件时，
 * 该命令会自动建立对应的文件结构来存放相应的class文件。
 * <p>
 * 如果直接使用javac Hello.java命令来编译这个文件，将会在当前路径下生成一个Hello.class文件，而不会生成lee文件夹。
 * 也就是说，如果编译Java文件时不使用-d选项，编译器不会为Java源文件生成相应的文件结构。
 * 鉴于此，推荐编译Java文件时总是使用-d选项，即使想把生成的class文件放在当前路径下，也应使用 -d . 选项，而不省略-d选项。
 * <p>
 * 进入out目录，运行 java lee.Hello 看到上面程序正常输出。
 * 如果进入out\lee路径下使用java Hello命令来运行Hello类，系统将提示错误。
 * 正如前面讲的，Hello类处于lee包下，因此必须把Hello.class文件放在lee路径下。
 * <p>
 * 当虚拟机要装载lee.Hello类时，它会依次搜索CLASSPATH环境变量所指定的系列路径，查找这些路径下是否包含lee路径，并在lee路径下查找是否包含Hello.class文件。
 * 虚拟机在装载带包名的类时，会先搜索CLASSPATH环境变量指定的目录，然后在这些目录中按与包层次对应的目录结构去查找class文件。
 * <p>
 * 也可以在外层目录运行: java -classpath out lee.Hello
 * <p>
 * 同一个包中的类不必位于相同的目录下，例如有lee.Person和lee.PersonTest两个类，
 * 它们完全可以一个位于C盘下某个位置，一个位于D盘下某个位置，只要让CLASSPATH环境变量里包含这两个路径即可。
 * 虚拟机会自动搜索CLASSPATH下的子路径，把它们当成同一个包下的类来处理。
 * <p>
 * 不仅如此，也应该把Java源文件放在与包名一致的目录结构下。
 * 与前面介绍的理由相似，如果系统中存在两个Hello类，通常也对应两个Hello.java源文件，如果把它们的源文件也放在对应的文件结构下，
 * 就可以解决源文件在文件系统中的存储冲突。
 * <p>
 * 注意：Java的包机制需要两个方面保证：① 源文件里使用package语句指定包名；② class文件必须放在对应的路径下。
 * <p>
 * package语句必须作为源文件的第一条非注释性语句，一个源文件只能指定一个包，即只能包含一条package语句，
 * 该源文件中可以定义多个类，则这些类将全部位于该包下。
 * <p>
 * 如果没有显式指定package语句，则处于默认包下。
 * 在实际企业开发中，通常不会把类定义在默认包下。
 * <p>
 * 同一个包下的类可以自由访问，例如下面的HelloTest类，如果把它也放在lee包下，则这个HelloTest类可以直接访问Hello类，无须添加包前缀。
 * <p>
 * 下面在lee包下再定义一个sub子包，并在该包下定义一个Apple空类。
 * 对于上面的lee.sub.Apple类，位于lee.sub包下，与lee.HelloTest类和lee.Hello类不再处于同一个包下，
 * 因此使用lee.sub.Apple类时就需要使用该类的全名（即包名加类名），即必须使用lee.sub.Apple写法来使用该类。
 * <p>
 * 虽然lee.sub包是lee包的子包，但在lee.Hello或lee.HelloTest中使用lee.sub.Apple类时，依然不能省略前面的lee包路径，
 * 即在lee.HelloTest类和lee.Hello类中使用该类时不可写成sub.Apple，必须写成完整包路径加类名：lee.sub.Apple。
 *
 * 提示：父包和子包之间确实表示了某种内在的逻辑关系，例如org.crazyit.elearnging父包和org.crazyit.elearning.student子包，
 * 确实可以表明后者是前者的一个模块。但父包和子包在用法上则不存在任何关系，如果父包中的类需要使用子包中的类，则必须使用子包的全名，而不能省略父包部分。
 */

public class Hello {
    public static void main(String[] args) {
        System.out.println("Hello World!");
//        lee.sub.Apple apple = new lee.sub.Apple();  // 必须写成完整包路径加类名：lee.sub.Apple。
    }
}
